#pragma

#include <iostream>
#include <string>
#include <memory>
#include <cerrno>
#include <functional>
#include <unordered_map>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Epoller.hpp"
#include "Socket.hpp"
#include "Comm.hpp"

// std::function使用std::weak_ptr<Connection>作为模板参数，那么您需要确保Connection类已经被定义
// 即在使用std::function之前，其模板参数已经被完整定义
class Connection;
class TcpServer;

uint32_t EVENT_IN = (EPOLLIN | EPOLLET); // 以边缘触发的方式去关心读事件就绪
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);
const static int g_buffer_size = 128; // 读取每个套接字输入缓冲区的缓冲区大小

using func_t = std::function<void(std::weak_ptr<Connection>)>; // 简化函数类型
using except_func = std::function<void(std::weak_ptr<Connection>)>;

class Connection
{
public:
    Connection(int sock) : _sock(sock) {}
    ~Connection() {}

    // 对每个连接fd所关心的不同事件的处理方法
    void SetHandler(func_t recv_cb, func_t send_cb, except_func except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    int SockFd() { return _sock; }

    void AppendInBuffer(const std::string &info)
    {
        _inbuffer += info;
    }

    void AppendOutBuffer(const std::string &info)
    {
        _outbuffer += info;
    }

    std::string &Inbuffer() // for debug
    {
        return _inbuffer;
    }

    std::string &OutBuffer()
    {
        return _outbuffer;
    }

    void SetWeakPtr(std::weak_ptr<TcpServer> tcp_server_ptr) // 显示设置成员变量
    {
        _tcp_server_ptr = tcp_server_ptr; // 两个对象互相持有对方的 std::shared_ptr，就会形成循环引用，所以使用weak_ptr
    }

private:
    int _sock;
    std::string _inbuffer;  // 读缓冲区
    std::string _outbuffer; // 写缓冲区

public:
    func_t _recv_cb;
    func_t _send_cb;
    except_func _except_cb;

    std::weak_ptr<TcpServer> _tcp_server_ptr;
    std::string _ip;
    uint16_t _port;
};

// 服务器的监听套接字和其他套接字等待事件就绪的工作交给epoll，每个套接字都有各自的用户级读、写缓冲区
// 与读、写、异常事件处理函数，套接字读取报文后，交给上层处理后
class TcpServer : public std::enable_shared_from_this<TcpServer>, public nocopy
{
    static const int num = 64;

public:
    TcpServer(uint16_t port, func_t OnMessage)
        : _port(port),
          _OnMessage(OnMessage),
          _quit(true),
          _epoller_ptr(new Epoller()),
          _listensock_ptr(new Sock())
    {
    }

    void Init()
    {
        _listensock_ptr->Socket();
        SetNonBlockOrDie(_listensock_ptr->Fd()); // 将文件描述符设置为非阻塞等待
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        lg(Info, "creat listen socket success:%d", _listensock_ptr->Fd());
        AddConnection(_listensock_ptr->Fd(), EVENT_IN, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr); // 让监听套接字对象与TcpServer::Accepter绑定，将this指针作为第一个参数传入Accepter成员函数
    }

    // 建立连接对象，将套接字加入epoll模型
    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cv, except_func except_cb, const std::string &ip = "0.0.0.0", uint16_t port = 0)
    {
        // 给sock也建立一个connection对象，将listensock添加到Connection中，同时，listensock和Connecion放入_connections
        std::shared_ptr<Connection> new_connection(new Connection(sock)); // 让智能指针管理连接
        new_connection->SetWeakPtr(shared_from_this());                   // shared_from_this(): 返回当前对象的shared_ptr
        new_connection->SetHandler(recv_cb, send_cv, except_cb);
        new_connection->_ip = ip;
        new_connection->_port = port;
        _connections.insert(std::make_pair(sock, new_connection)); // 添加到哈希表
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, event);   // 将套接字与其要关心的事件添加到epoll模型的红黑树中
    }

    void Accepter(std::weak_ptr<Connection> conn) // tcpserver持有connection对象，参数使用std::weak_ptr，避免循环引用
    {
        auto connection = conn.lock(); // 从 std::weak_ptr 中获取一个 std::shared_ptr
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = accept(connection->SockFd(), (struct sockaddr *)&peer, &len); // 获取连接套接字和客户端套接字地址
            if (sock > 0)
            {
                uint16_t peerport = ntohs(peer.sin_port); // 将客户端口号由网络字节序列转化为主机字节序列
                char ipbuf[128];
                inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf)); // 将网络地址从网络字节序转换为点分十进制的字符串形式字符串形式
                lg(Debug, "get a new client, get info-> [%s:%d], sockfd : %d", ipbuf, peerport, sock);
                SetNonBlockOrDie(sock); // 把每个套接字设置为非阻塞等待，可以避免因阻塞等待而导致错过其他事件的通知
                // listensock只需要设置_recv_cb, 而其他sock，需要设置读，写，异常事件就绪的处理函数
                AddConnection(sock, EVENT_IN || EVENT_OUT,
                              std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                              std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                              std::bind(&TcpServer::Excepter, this, std::placeholders::_1),
                              ipbuf, peerport); // TODO
            }
            else
            {
                if (errno == EWOULDBLOCK) // 表示没有新的连接请求可用，监听套接字的读事件处理完毕
                    break;
                else if (errno == EINTR) // 系统调用被中断，继续循环尝试再次调用accept
                    continue;
                else
                    break;
            }
        }
    }

    // 事件管理器
    // 应不应该关心数据的格式？？？不应该！！服务器只要IO数据就可以，有没有读完，报文的格式细节，你不用管。
    void Recver(std::weak_ptr<Connection> conn) // 常关心读事件
    {
        if (conn.expired())
            return;
        // std::weak_ptr 不保证它指向的对象仍然存在,且不增加它指向的对象的引用计数，如果没有任何std::shared_ptr指向同一个对象，
        // 该对象将在最后一个std::shared_ptr销毁时被销毁，如果std::weak_ptr能够直接访问对象，那么这可能导致对象在被访问的已经被销毁的情况
        auto connection = conn.lock();
        int sock = connection->SockFd();
        while (true)
        {
            char buffer[g_buffer_size]; // 将每个socket的输入缓冲区读入buffer
            memset(buffer, 0, sizeof(buffer));
            ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); // 非阻塞读取
            if (n > 0)
            {
                connection->AppendInBuffer(buffer); // 向对应连接fd的的读缓冲区拷贝
            }
            else if (n == 0)
            {
                lg(Info, "sockfd: %d, client info %s:%d quit...", sock, connection->_ip.c_str(), connection->_port);
                connection->_except_cb(connection);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    lg(Warning, "sockfd: %d, client info %s:%d recv error...", sock, connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        // 数据有了，但是不一定全，1. 检测 2. 如果有完整报文，就处理
        _OnMessage(connection); // 你读到的sock所有的数据connection
    }

    void Excepter(std::weak_ptr<Connection> connection)
    {
        if (connection.expired())
            return;
        auto conn = connection.lock();
        int fd = conn->SockFd();
        lg(Warning, "Excepter hander sockfd: %d, client info %s:%d excepter handler", conn->SockFd(), conn->_ip.c_str(), conn->_port);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0); // 移除对特定fd的关心
        lg(Debug, "close %d from _connections...\n", fd);
        close(fd); // 关闭异常的文件描述符
        lg(Debug, "remove %d from _connections...\n", fd);
        _connections.erase(fd); // 从哈希表中移除sockfd与连接
    }

    // 写事件一般都处于就绪状态，因为fd写缓冲区一般都为空，为了减少服务器对无效写事件就绪处理，
    // 如果套接字的用户写缓冲区无数据，取消对该套接字的关心，按需设置
    void Sender(std::weak_ptr<Connection> conn)
    {
        if (conn.expired())
            return;
        auto connection = conn.lock();
        auto &outbuffer = connection->OutBuffer();
        while (true)
        {
            ssize_t n = send(connection->SockFd(), outbuffer.c_str(), outbuffer.size(), 0);
            if (n > 0)
            {
                outbuffer.erase(0, n);
                if (outbuffer.empty())
                    break;
            }
            else if (n == 0)
            {
                return;
            }
            else
            {
                lg(Warning, "sockfd: %d,client info %s:%d send error...", connection->SockFd(), connection->_ip, connection->_port);
                connection->_except_cb(connection);
                return;
            }
        }
        // 异常时，写事件没处理完毕
        if (!outbuffer.empty())
        {
            // 关心套接字的写事件
            EnableEvent(connection->SockFd(), true, true);
        }
        else
        {
            // 关闭对写事件的关心
            EnableEvent(connection->SockFd(), true, false);
        }
    }

    // 在哈希表中的连接是安全的，异常连接从哈希表中移除
    bool IsConnectionSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if (iter == _connections.end())
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    void EnableEvent(int sock, bool readable, bool writeable)
    {
        uint32_t events;
        events |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_MOD, sock, events);
    }

    void Dispatcher(int timeout) // 就绪事件派发，每个套接字有自己对应的方法处理就绪事件
    {
        int n = _epoller_ptr->EpollerWait(revs, num, timeout);
        for (int i = 0; i < n; i++)
        {
            uint32_t events = revs[i].events;
            int sock = revs[i].data.fd;
            if ((events & EPOLLIN) && IsConnectionSafe(sock))
            {
                if (_connections[sock]->_recv_cb)
                {
                    _connections[sock]->_recv_cb(_connections[sock]);
                }
            }
            if ((events & EPOLLOUT) && IsConnectionSafe(sock))
            {
                if (_connections[sock]->_send_cb)
                    _connections[sock]->_send_cb(_connections[sock]);
            }
        }
    }

    void Loop()
    {
        _quit = false;
        // AddConnection();
        while (!_quit)
        {
            // Dispatcher(3000);
            Dispatcher(-1);
            PrintConnection();
        }
        _quit = true;
    }

    // 遍历哈希表_connections映射中的所有连接及其输入缓冲区中的内容
    void PrintConnection()
    {
        std::cout << "_connections fd list: ";
        for (auto &connection : _connections)
        {
            std::cout << connection.second->SockFd() << ", ";
            std::cout << "inbuffer: " << connection.second->Inbuffer().c_str();
        }
        std::cout << std::endl;
    }

    ~TcpServer()
    {
    }

private:
    std::shared_ptr<Epoller> _epoller_ptr;                             // 指向epoll类的指针，通过智能指针对epoll模型进行操作
    std::shared_ptr<Sock> _listensock_ptr;                             // 指向监听套接字的智能指针
    std::unordered_map<int, std::shared_ptr<Connection>> _connections; // <socket,Connection>套接字与其对应连接
    struct epoll_event revs[num];                                      // 就绪事件数组
    uint16_t _port;
    bool _quit;        // 判断服务器是否停止服务
    func_t _OnMessage; // 让上层处理信息
};